﻿using Library.Cache;
using PostSharp.Aspects;
using Share.Enterprise.DI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Core.Aspects
{
    [Serializable]
    public abstract class CacheActionAttribute : MethodInterceptionAspect
    {        
        public string Name { get; set; }

        [NonSerialized]
        protected ICacheStorage cacheStorage;
        public override void RuntimeInitialize(MethodBase method)
        {
            cacheStorage = DIContext.CurrentContainer.GetInstance<ICacheStorage>();
        }        

        protected string GetCacheKey(MethodInterceptionArgs args)
        {
            string rootKey;
            if (!string.IsNullOrEmpty(Name))
                rootKey = Name;
            else
            {
                var cacheKeyAttribute = args.Instance.GetType().GetCustomAttribute<CacheNameAttribute>();
                if (cacheKeyAttribute != null)
                    rootKey = cacheKeyAttribute.Name;
                else
                    rootKey = args.Instance.GetType().Name;
            }                        
            
            return string.Format("{0}{1}", rootKey, GetArgumentsCacheKey(args.Arguments));
        }

        protected string GetArgumentsCacheKey(Arguments arguments)
        {
            StringBuilder key = new StringBuilder();
            for (int i = 0; i < arguments.Count; i++)
                if (arguments[i].GetType().IsPrimitive || arguments[i].GetType() == typeof(String))
                {
                    key.Append("_");
                    key.Append(arguments[i]);
                }

            return key.ToString();
        }
    }

    [Serializable]
    public class CacheAttribute : CacheActionAttribute
    {
        public bool NoExpiration { get; set; }

        /// <summary>
        /// In Seconds
        /// </summary>
        public int Duration { get; set; }

        public override void OnInvoke(MethodInterceptionArgs args)
        {
            TimeSpan? timeSpan = null;
            if (Duration > 0)
                timeSpan = TimeSpan.FromSeconds(Duration);
            if(NoExpiration)
                timeSpan = TimeSpan.FromDays(30);

            string key = GetCacheKey(args);           
            args.ReturnValue = cacheStorage.Get<object>(key, () =>
            {
                args.Proceed();
                return args.ReturnValue;
            }, timeSpan);
        }
    }

    [Serializable]
    public class DeleteCacheAttribute : CacheActionAttribute
    {
        public override void OnInvoke(MethodInterceptionArgs args)
        {
            string key = GetCacheKey(args);
            cacheStorage.Remove(key);
            args.Proceed();
        }
    }

    [AttributeUsageAttribute(AttributeTargets.Class, AllowMultiple = false)]
    public class CacheNameAttribute : Attribute
    {
        public string Name { get; set; }
        public CacheNameAttribute(string name)
        {
            this.Name = name;
        }
    }
}
